package miage.rest.resource.candidature;

import com.thoughtworks.xstream.XStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.Date;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import javax.mail.internet.InternetAddress;
import miage.rest.db.Database;
import miage.rest.model.Candidat;
import miage.rest.model.Candidature;
import miage.rest.model.SessionDiplome;
import miage.rest.xml.XstreamRepresentation;
import miage.rest.xml.xstream.converter.HrefConverter;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.resource.Get;
import org.restlet.resource.Put;
import org.restlet.resource.ResourceException;
import org.restlet.resource.ServerResource;
import miage.rest.routing.UriTemplateFinder;
import miage.rest.form.Form;
import miage.rest.mail.MailFactory;
import miage.rest.resource.ResourceError;
import miage.rest.validation.DateValidator;
import miage.rest.validation.LengthValidator;
import miage.rest.validation.LongRangeValidator;
import miage.rest.validation.NotEmptyValidator;
import miage.rest.validation.RegexValidator;
import miage.rest.validation.Validator;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.mail.Email;
import org.restlet.data.Method;
import org.restlet.representation.EmptyRepresentation;
import org.restlet.resource.Post;
import org.restlet.util.WrapperRepresentation;

/**
 *
 * @author marya
 */
public class CandidaturesResource extends ServerResource {

    private final Database database;
    private UriTemplateFinder uriTemplateFinder;
    private MailFactory mailFactory;
    private SessionDiplome session;

    public CandidaturesResource(Database database, UriTemplateFinder uriTemplateFinder, MailFactory mailFactory) {
        super();

        this.database = database;
        this.uriTemplateFinder = uriTemplateFinder;
                this.mailFactory = mailFactory;
    }

    /** Initialisation de la ressource a partir de la chaine d'url */
    @Override
    protected void doInit() throws ResourceException {

        Long idSession = null;
        final String sqlSession = "SELECT id_session FROM session_diplome WHERE id_session = ?";
        String idSessionAttribute = getRequest().getAttributes().get("idSession").toString();
        LongRangeValidator longRangeValidator = new LongRangeValidator(1l, null);
        //Validation de l'id de la session
        if (longRangeValidator.isValid(idSessionAttribute)) {
                idSession = Long.parseLong(idSessionAttribute);
        } else {
                getResponse().setEntity(ResourceError.getNewRepresentation("invalidSessionId"));
                throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST);
        }
        //Vérification d'existence de la session
        try {
            session = database.fetchRow(sqlSession, SessionDiplome.class, idSession);
            if (session == null) {
                setExisting(false);
            }
        } catch (SQLException ex) {
                throw new ResourceException(Status.SERVER_ERROR_INTERNAL, ex);
        }

    }

    

    /** Représentation XML en réponse à une requête GET.
     * Jeu de caractères UTF-8
     */
    @Get
    public Representation headOrGet() throws Exception{
        Representation response = null;
        System.out.print("CandidaturesResource::");
        /**
         * Restlet ne permet pas de gérer la méthode HEAD
         */
        if (getRequest().getMethod().equals(Method.HEAD)) {
            System.out.println("HEAD");
            response = doHead();
        } else if (getRequest().getMethod().equals(Method.GET)) {
            System.out.println("GET");
            response = doGet();
        }
        return response;
    }


    /**
     * Traitement de la requête HTTP HEAD
     * @return La date de dernière modification des candidatures
     */
    public Representation doHead() throws Exception {
        Representation response = null;
        String sqlLastAccess = "SELECT c.date_effet FROM candidature AS c WHERE c.date_effet <> '' AND c.id_session = " + session.getIdSession() + " ORDER BY c.date_effet DESC LIMIT 0, 1";
        Date lastAccessDate = (Date) database.fetchOne(sqlLastAccess);

        if (lastAccessDate != null) {
            WrapperRepresentation wr = new WrapperRepresentation(new EmptyRepresentation());
            wr.setModificationDate(new java.util.Date(lastAccessDate.getTime()));

                            response = wr;
            setStatus(Status.SUCCESS_OK);
        } else {
            setStatus(Status.CLIENT_ERROR_NOT_FOUND);
            response = ResourceError.getNewRepresentation("Candidatures non trouvées pour cette session");
        }
        
        return response;
    }





    /** Reponse a une requete GET */
    public Representation doGet() throws Exception{
        Representation response;
        List<Candidature> candidatures = null;

        String sqlSessionDiplome = "SELECT id_candidature,id_candidat,id_statut FROM candidature WHERE id_session = " + session.getIdSession();
        candidatures = database.fetchAll(sqlSessionDiplome, Candidature.class);

        if (candidatures != null) {
            /**
             * Pour chaque candidature dans la liste des SessionDiplome de la session
             * on ne peut initialiser que idCandidat
             * Il faut donc qu'on mette à jour l'attribut candidat de chaque candidature
             */
            Iterator it = candidatures.iterator();
            while (it.hasNext()) {
                Candidature c = (Candidature) it.next();
                c.setCandidat(new Candidat(c.getIdCandidat()));
            }
            session.setCandidatures(candidatures);

            //Création d'une représentation XStream pour chaque candidature
            response = new XstreamRepresentation<SessionDiplome>(session);
            XStream xstream = ((XstreamRepresentation) response).getXstream();

            //Ajout de l'intégration des liens href  pour la classe Candidature
            //par l'intermédiaire de la classe HrefConverter
            xstream.registerConverter(
                    new HrefConverter(
                    Candidature.class,
                    xstream.getConverterLookup(),
                    uriTemplateFinder.find(CandidatureResource.class)));

            //Ajout de l'intégration des liens href  pour la classe Candidat
            //par l'intermédiaire de la classe HrefConverter
            xstream.registerConverter(
                    new HrefConverter(
                    Candidat.class,
                    xstream.getConverterLookup(),
                    uriTemplateFinder.find(CandidatResource.class)));

        } else {
            setStatus(Status.CLIENT_ERROR_NOT_FOUND);
            response = ResourceError.getNewRepresentation("Candidatures non trouvées pour cette session");
        }
        return response;
    }
    
    /** Reponse a une requete PUT */
    @Put
    public String doPut(Representation entity) throws IOException {
        return "";
    }

    @Post
    public Representation doPost(Representation entity) throws Exception{
            Representation result=null;
            Connection connexion= null;
            List<ResourceError> errors = new ArrayList<ResourceError>();
            try{
                    
                Form form = getNewCandidatureForm(entity);
                if(!form.isValid()){
                    
                    for(String error : form.getMessages())
                        errors.add(new ResourceError(error));
                    setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
                }else{
                    //Première candidature donc création d'un candidat
                    String nom= form.getFirstValue("nom");
                    String prenom= form.getFirstValue("prenom");
                    String tel1= form.getFirstValue("tel1");
                    String tel2= form.getFirstValue("tel2");
                    String email= form.getFirstValue("email");
                    String commentaire= form.getFirstValue("commentaire");
                    DateFormat date = new SimpleDateFormat("d/M/y");
                    Date date_naissance = new Date(date.parse(form.getFirstValue("dateNaissance")).getTime());
                    String lieuNaissance= form.getFirstValue("lieuNaissance");
                    String nomMarital= form.getFirstValue("nomMarital");
                    String adresse= form.getFirstValue("adresse");
                    String codePostal= form.getFirstValue("codePostal");
                    String ville= form.getFirstValue("ville");
                    String idNationalite= form.getFirstValue("idNationalite");
                    String sexe= form.getFirstValue("sexe");

                    connexion= database.getNewConnection();
                    connexion.setAutoCommit(false);
                    String sqlCandidat= "INSERT INTO candidat(" +
                            "nom,prenom,email,adresse,code_postal,ville,lieu_naissance,nom_marital,tel1,tel2,date_naissance,id_nationalite,sexe) " +
                            " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)";
                    Integer insertedCandidat = database.update(connexion, sqlCandidat,nom,prenom,email,adresse,codePostal,ville,lieuNaissance,nomMarital,tel1,tel2,date_naissance,idNationalite,sexe).getGeneratedKeys();
                    if(insertedCandidat == null || insertedCandidat < 1){
                        errors.add(new ResourceError("Impossible de créer le candidat"));
                        setStatus(Status.SERVER_ERROR_INTERNAL);
                    }else{
                        String sqlCandidature ="INSERT INTO candidature(id_candidat,id_session,id_statut,commentaire,date_effet)" +
                            " VALUES(?,?,?,?,NOW())";
                       Integer insertedCandidature = database.update(connexion, sqlCandidature, insertedCandidat,session.getIdSession(),4,commentaire).getGeneratedKeys();
                        if(insertedCandidature == null || insertedCandidature < 1){
                            errors.add(new ResourceError("Impossible de créer le candidature"));
                            setStatus(Status.SERVER_ERROR_INTERNAL);
                        }else{
                            result =   new XstreamRepresentation<Candidature>(new Candidature(insertedCandidature));
                            XStream xstream = ((XstreamRepresentation) result).getXstream();
                            xstream.registerConverter(new HrefConverter(
                                    Candidature.class,
                                    xstream.getConverterLookup(),
                                    uriTemplateFinder.find(CandidatureResource.class)));

                            setStatus(Status.SUCCESS_CREATED);
                            sendMail(email);
                            connexion.commit();
                        }
                    }
                }
                    
            }
            
            catch (Exception exc) {
               DbUtils.rollbackAndCloseQuietly(connexion);
              if (exc instanceof NumberFormatException) {
                  setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
              }else{
                  throw  exc;
              }
            }
            if(!errors.isEmpty())
                result = ResourceError.getNewRepresentation(errors);
            return result;
    }

    @Override
    protected void doCatch(Throwable throwable) {
        super.doCatch(throwable);
    }




    @SuppressWarnings("unchecked")
    private Form getNewCandidatureForm(Representation entity){
        Form form = new Form(entity);
        Validator<String> notEmptyValidator = new NotEmptyValidator();
        Validator<String> lentghValidator100 = new LengthValidator(100);
        Validator<String> lentghValidator50 = new LengthValidator(50);
        Validator<String> namesValidator = new RegexValidator("^([a-zA-Z'àâéèêëîïôùûçÀÂÉÈÔÙÛÇ\\s-]{1,100})$");
        Validator<String> emailValidator = new RegexValidator("^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$");
        Validator<String> dateValidator = new DateValidator("d/m/y", "jj/mm/aaaa");
        //Récupération du nom, prénom et ajout de validateurs pour tester si le nom est vide et s'il valide une regex
        form.addValidators("nom", notEmptyValidator, namesValidator);
        form.addValidators("prenom", notEmptyValidator, namesValidator);
        form.addValidator("tel1", lentghValidator50);
        form.addValidator("tel2", lentghValidator50);
        form.addValidators("email", notEmptyValidator, lentghValidator50, emailValidator);
        form.addValidators("dateNaissance", notEmptyValidator, dateValidator);
        form.addValidator("lieuNaissance", lentghValidator100);
        form.addValidator("nomMarital", lentghValidator100);
        form.addValidator("adresse", notEmptyValidator);
        form.addValidators("codePostal", notEmptyValidator, new LengthValidator(10));
        form.addValidators("ville", notEmptyValidator, lentghValidator50);
        form.addValidator("idNationalite", new LengthValidator(5));
        form.addValidator("sexe", new LengthValidator(20));
        form.addValidator("commentaire", new LengthValidator(250));
        return form;
    }


    private void sendMail(String email){
        try{
            Email emailToSend = mailFactory.create();
            emailToSend.setSubject("Candidature à la formation MIAGE de Paris Descartes");
            emailToSend.setMsg("Nouvelle Candidature");
            emailToSend.setFrom("test@miage.rest");
            List<InternetAddress> addresses = new ArrayList<InternetAddress>();
            addresses.add(new InternetAddress(email));
            emailToSend.setTo(addresses);
            emailToSend.send();
        }
        catch (Exception exc) {
            this.getLogger().log(Level.WARNING, "Impossible d'envoyer le mail",exc);
        }
    }

}



